package cc.verywell.pureblock.net;

import java.net.SocketAddress;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;

/**
* 
* Copyright © 2020 TanYaqiu. All rights reserved.
* @author TanYaqiu 
* @address flat 601
* @date 2020年2月18日 下午11:07:21
*/
public class AioClientDelayer implements Predicate<SocketAddress> {

	@Override
	public boolean test(SocketAddress t) {
		return isConnected(t);
	}
	///////////////////////黑名单类
	private Map<SocketAddress, Long> connectedMap = new ConcurrentHashMap<SocketAddress, Long>();
	private AtomicLong connectedMin = new AtomicLong(0);
	private boolean isConnected(SocketAddress sa) {
		final long TIMEOUT = AioProvider.RECONNECT_TIME_OUT;
		long time = System.currentTimeMillis();
		long last = connectedMin.get()-time;
		if(last>0 && last<TIMEOUT) {
			//if(Pblog.DEBUG)Pblog.DEBUG("isConected normal ",time," remote: ",sa," last: ",last," timeout: ",TIMEOUT);
			return null!=connectedMap.putIfAbsent(sa, time+TIMEOUT);
		}
		synchronized (connectedMap) {
			last = connectedMin.get();
			if(last==0) {//还没有记录
				//if(Pblog.DEBUG)Pblog.DEBUG("isConected first ",time," remote: ",sa," timeout: ",TIMEOUT);
				time += TIMEOUT;
				connectedMap.put(sa,time);
				connectedMin.set(time);
				return false;
			}
			if(time<last) {
				//if(Pblog.DEBUG)Pblog.DEBUG("isConected timechanged ",time," remote: ",sa," last: ",last," timeout: ",TIMEOUT);
				time += TIMEOUT;
				if(time<last-TIMEOUT) {//时钟发生更改
					connectedMap.clear();
					connectedMap.put(sa,time);
					connectedMin.set(time);
					return false;
				}
				return null!=connectedMap.putIfAbsent(sa, time);
			}
			//else 需要清理已经超时的记录
			/*
			 * 只 putIfAbsnet的情况用removeIf, put情况用remove(key,value)才不会误删
			 * 参考 https://stackoverflow.com/questions/29876083/behavior-of-entryset-removeif-in-concurrenthashmap
			 */
			Long hasOld = connectedMap.putIfAbsent(sa, time+TIMEOUT);
			//if(Pblog.DEBUG)Pblog.DEBUG("isConected overtime ",time," remote: ",sa," last: ",last," timeout: ",TIMEOUT," hasOld: ",hasOld);
			long min = 0;
			Iterator<Map.Entry<SocketAddress, Long>> each = connectedMap.entrySet().iterator();
	        while (each.hasNext()) {
	        	Map.Entry<SocketAddress, Long> e = each.next();
	        	if(e.getValue()>time) {
	        		if(min==0 || min>e.getValue())
	        			min=e.getValue();
	        	}else {
	                each.remove();
	            }
	        }
	        connectedMin.set(min);
			return hasOld!=null && hasOld>time; 
		}
	}


}
